﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using CombatAutomationTheater;
using GameBoard;

namespace XMLCharSheets
{
    [DataContract]
    public class PlacedLights
    {

        [DataMember]
        public PathfinderLightType EnvironmentLight = new PathfinderLightType(PathfinderLightType.LightType.Disabled);

        [DataMember]
        private Dictionary<Guid, List<PathfinderLightSource>> LightsByOwner = new Dictionary<Guid, List<PathfinderLightSource>>();
        
        public List<PathfinderLightSource> AllLights
        {
            get
            {
                var ret = new List<PathfinderLightSource>();
                foreach (var cur in LightsByOwner)
                {
                    ret.AddRange(cur.Value);
                }
                return ret;
            }
        }

        internal void RegisterLightToOwner(Guid ownerID, PathfinderLightSource curLight)
        {
            if (!LightsByOwner.ContainsKey(ownerID))
            {
                LightsByOwner.Add(ownerID, new List<PathfinderLightSource>());
            }
            LightsByOwner[ownerID].Add(curLight);
            CheckLightSources();
        }

        internal void RemoveLightByOwner(Guid ownerID)
        {
            if (LightsByOwner.ContainsKey(ownerID))
            {
                LightsByOwner.Remove(ownerID);
            }
            CheckLightSources();
        }

        public List<PathfinderCharacter> GetActiveRosterAsPathfinder()
        {
            List<PathfinderCharacter> ret = new List<PathfinderCharacter>();
            foreach (var curChar in CombatService.RosterViewModel.ActiveRoster)
            {
                ret.Add(curChar as PathfinderCharacter);
            }
            return ret;
        }
        private List<PathfinderCharacter> GetSelectedAsPathfinder()
        {
            var selectedCharacters = CombatService.RosterViewModel.GetActiveList();
            List<PathfinderCharacter> cr = new List<PathfinderCharacter>();
            foreach (var cur in selectedCharacters)
            {
                if (cur is PathfinderCharacter)
                {
                    cr.Add(cur as PathfinderCharacter);
                }
            }
            return cr;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="livingRoster">The living roster is all visible creatures that should be checked for visibility</param>
        /// <param name="selectedRoster">The selected characters are the ones doing the looking</param>
        public void CheckLightSources(List<PathfinderCharacter> livingRoster = null, List<PathfinderCharacter> selectedRoster = null)
        {
            if (livingRoster == null)
            {
                livingRoster = GetActiveRosterAsPathfinder();
            }
            if (selectedRoster == null)
            {
                selectedRoster = GetSelectedAsPathfinder();
            }
            if(EnvironmentLight.CurrentLightType == PathfinderLightType.LightType.Disabled)
            {
                foreach (var cur in livingRoster.Where(x => x.InLight.CurrentLightType != PathfinderLightType.LightType.Normal))
                {
                    cur.InLight = new PathfinderLightType(PathfinderLightType.LightType.Normal);
                    VisualsViewModel.Instance.SetCharactersToVisibleStatus(
                        cur.FirstVisual.UniqueID, BoardsViewModel.BoardTypes.Window, VisiblityType.Visible);
                }
                return;
            }
            var hasLowlight = selectedRoster.Where(
                (x =>
                    x.CharacterVisionType.CurrentVisionType == CharacterVisionType.VisionType.LowLight ||
                    x.CharacterVisionType.CurrentVisionType == CharacterVisionType.VisionType.LowLightAndDarkVision
                    )).ToList();

            Dictionary<PathfinderCharacter, PathfinderLightType> charactersInLight = new Dictionary<PathfinderCharacter, PathfinderLightType>();

            foreach (var cur in livingRoster)
            {
                charactersInLight.Add(cur, new PathfinderLightType(PathfinderLightType.LightType.NoneSet));
            }
            foreach (var curLight in AllLights)
            {
                var primaryLight = curLight.PrimaryLightInfo;
                var secondaryLight = curLight.SecondaryLightInfo;
                var primaryRadius = primaryLight.Radius;
                double secondaryRadius = secondaryLight.Radius;
                if (hasLowlight.Any() && primaryLight.SelectedLight.IsBrighter(EnvironmentLight))
                {
                    primaryRadius *= 2;
                    secondaryRadius *= 2;
                }
                foreach (var cur in livingRoster)
                {
                    var distFromLight = (curLight.Location - cur.Location).Length;
                    if (distFromLight < primaryRadius)
                    {
                        charactersInLight[cur] = primaryLight.CombineLights(EnvironmentLight);
                    }
                    else if (distFromLight < secondaryRadius)
                    {
                        charactersInLight[cur] = secondaryLight.CombineLights(EnvironmentLight);
                    }
                }
            }
            //Generally, placed light effects are intended to overwrite the light effects of the environment
            var unaffectedByPlacedLights =
                charactersInLight.Where(x => x.Value.CurrentLightType == PathfinderLightType.LightType.NoneSet)
                                 .Select(q => q.Key)
                                 .ToList();
            foreach (
                var cur in unaffectedByPlacedLights
                    )
            {
               charactersInLight[cur] = EnvironmentLight.Copy();
            }
            foreach (var cur in selectedRoster.Where(
                x => x.CharacterVisionType.CurrentVisionType == CharacterVisionType.VisionType.Darkvision ||
                x.CharacterVisionType.CurrentVisionType == CharacterVisionType.VisionType.LowLightAndDarkVision
                ))
            {
                var currentlyInDarkness = charactersInLight.Where(x => 
                    x.Value.CurrentLightType == PathfinderLightType.LightType.Dark
                    ||x.Value.CurrentLightType == PathfinderLightType.LightType.Dim
                    
                    ).ToList();
                foreach (var curOther in currentlyInDarkness)
                {
                    var dist = (cur.Location - curOther.Key.Location).Length;
                    if (dist < cur.CharacterVisionType.Distance)
                    {
                        //Set current character to 'normal light', if it's brighter.
                        charactersInLight[curOther.Key] = PathfinderLightType.Brighter(
                            curOther.Value,
                            new PathfinderLightType(PathfinderLightType.LightType.Normal));
                    }
                }
            }
            foreach (var cur in charactersInLight)
            {
                cur.Key.InLight = cur.Value;
                var currentVisType = VisiblityType.Visible;
                switch (cur.Key.InLight.CurrentLightType)
                {
                    case PathfinderLightType.LightType.Dark:
                        currentVisType = VisiblityType.Invisible;
                        break;
                    case PathfinderLightType.LightType.Dim:
                        currentVisType = VisiblityType.Translucent;
                        break;
                    case PathfinderLightType.LightType.Normal:
                    case PathfinderLightType.LightType.Bright:
                        currentVisType = VisiblityType.Visible;
                        break;
                }
                if (cur.Key.HasVisual)
                {
                    if (!cur.Key.IsInvisibleOnAtLeastOneBoard)
                    {
                        VisualsViewModel.Instance.SetCharactersToVisibleStatus(cur.Key.FirstVisual.UniqueID, BoardsViewModel.BoardTypes.Window, currentVisType);
                    }
                }
                cur.Key.NotifyStatusChange();
            }
        }
    }
}
